#define MOVEMENT_NONE 0
#define MOVEMENT_FACE 1
#define MOVEMENT_MOVE 2
#define MOVEMENT_ARRIVED 3
#define MOVEMENT_STUCK 4
#define MOVEMENT_WAIT 5
#define MOVEMENT_OBSTACLE 6
#define MOVEMENT_STOP 7

objectdef Movement inherits BotBase
{
	variable int CurrentState = 0
	variable bool Moving = FALSE
	variable bool StopMovement = FALSE
	variable bool StopObstacle = FALSE
	variable float Precision
	variable string Mode = "point"
	variable bool From = FALSE
	variable point3f Start
	variable point3f Destination
	variable point3f Temp
	variable int TempCounter = 0
	variable int LastStuckTime = ${LavishScript.RunningTime}
	variable int stuck = 0
	variable int LastObstacleCheck = ${LavishScript.RunningTime}
	variable int WaitUntil = ${LavishScript.RunningTime}

	method Face(float X, float Y)
	{
		face ${X} ${Y}
	}

	method FaceTarget()
	{
		if ${Me.Target(exists)}
		{
			This:Face[${Me.Target.X},${Me.Target.Y}]
		}
	}

	method Stop()
	{
		VG:ExecBinding[MoveForward,release]
		VG:ExecBinding[MoveBackward,release]
		VG:ExecBinding[TurnLeft,release]
		VG:ExecBinding[TurnRight,release]
		VG:ExecBinding[StrafeLeft,release]
		VG:ExecBinding[StrafeRight,release]
	}

	method ResetKeys()
	{
		VG:ExecBinding[TurnLeft,release]
		VG:ExecBinding[TurnRight,release]
		VG:ExecBinding[StrafeLeft,release]
		VG:ExecBinding[StrafeRight,release]
	}

	method Turn()
	{
		if ${Math.Rand[10]} < 5
		{
			if ${Math.Rand[10]} < 5
			{
				VG:ExecBinding[TurnLeft]
			}
			else
			{
				VG:ExecBinding[TurnRight]
			}
		}
		else
		{
			if ${Math.Rand[10]} < 5
			{
				VG:ExecBinding[TurnLeft]
			}
			else
			{
				VG:ExecBinding[TurnRight]
			}
		}
	}

	method Strafe()
	{
		if ${Math.Rand[10]} < 5
		{
			if ${Math.Rand[10]} < 5
			{
				VG:ExecBinding[StrafeLeft]
			}
			else
			{
				VG:ExecBinding[StrafeRight]
			}
		}
		else
		{
			if ${Math.Rand[10]} < 5
			{
				VG:ExecBinding[StrafeLeft]
			}
			else
			{
				VG:ExecBinding[StrafeRight]
			}
		}
	}

	method TurnStrafe()
	{
		if ${Math.Rand[10]} < 5
		{
			This:Strafe
		}
		else
		{
			This:Turn
		}
	}

	method RandomShuffle()
	{
		if ${Math.Rand[100]} < 1
		{
			This:EA["Random Shuffle | Jump"]
			This:Jump
		}
		if ${Math.Rand[100]} < 2
		{
			This:EA["Random Shuffle | Turnstafe"]
			This:TurnStrafe
		}
	}

	method Jump()
	{
		VG:ExecBinding[Jump]
		VG:ExecBinding[Jump,release]
	}

	member HaveDestination()
	{
		if ${This.Destination.X} == 0 && ${This.Destination.Y} == 0
		{
			return FALSE
		}
		return TRUE
	}
	
	member Arrived()
	{
		if !${This.HaveDestination}
		{
			return FALSE
		}
	
		if ${This.From}
		{
			if ${Math.Distance[${Me.X},${Me.Y},${This.Destination.X},${This.Destination.Y}]} >= ${This.Precision}
			{
				This.Moving:Set[FALSE]
				return TRUE
			}
		}
		else
		{
			if ${Math.Distance[${Me.X},${Me.Y},${This.Destination.X},${This.Destination.Y}]} <= ${This.Precision}
			{
				This.Moving:Set[FALSE]
				return TRUE
			}
		}
		
		return FALSE
	}
	
	member NeedMovement()
	{
		if !${This.HaveDestination}
		{
			return FALSE
		}
		
		if ${This.Arrived}
		{
			return FALSE
		}
		
		return TRUE
	}

	member GotObstacle()
	{
		if ${Math.Distance[${Me.X}, ${Me.Y}, ${This.Temp.X}, ${This.Temp.Y}]} < 0.5 && ${Math.Distance[${Me.X}, ${Me.Y}, ${This.Destination.X}, ${This.Destination.Y}]} >= 10
		{
			RETURN TRUE
		}
		else
		{
			RETURN FALSE
		}
	}

	method AvoidObstacle()
	{
		This:EA["Avoiding Obstacle"]
		This:TurnStrafe
		This.WaitUntil:Set[${Math.Calc[${LavishScript.RunningTime} + ${Math.Rand[1500]:Inc[1000]}]}]
	}

	method MovePulse()
	{
		This:ResetKeys
		if ${This.Mode.Equal[target]}
		{
			This.Destination.X:Set[${Me.Target.X}]
			This.Destination.Y:Set[${Me.Target.Y}]
		}
		This:Face[${This.Destination.X},${This.Destination.Y}]
		;if !${Me.Moving}
		;{
			if ${This.From}
			{
				;This:Stop
				VG:ExecBinding[MoveBackward]
			}
			else
			{
				;This:Stop
				VG:ExecBinding[MoveForward]
			}
		;}
		This:Face[${This.Destination.X},${This.Destination.Y}]
		if ${This.From}
		{
			if ${Math.Distance[${Me.X},${Me.Y},${This.Destination.X},${This.Destination.Y}]} >= ${This.Precision}
			{
				This.Start.X:Set[${Me.X}]
				This.Start.Y:Set[${Me.Y}]
			}
		}
		else
		{
			if ${Math.Distance[${Me.X},${Me.Y},${This.Destination.X},${This.Destination.Y}]} <= ${This.Precision}
			{
				This.Start.X:Set[${Me.X}]
				This.Start.Y:Set[${Me.Y}]
			}
		}
		This:Face[${This.Destination.X},${This.Destination.Y}]
		if ${This.TempCounter} == 300
		{
			This.Temp.X:Set[${Me.X}]
			This.Temp.Y:Set[${Me.Y}]
			This.TempCounter:Set[0]
		}
		This.TempCounter:Inc
		This:Face[${This.Destination.X},${This.Destination.Y}]
	}

	member State()
	{
		if !${Me(exists)}
		{
			return MOVEMENT_WAIT
		}
	
		if ${LavishScript.RunningTime} < ${This.WaitUntil}
		{
			return MOVEMENT_WAIT
		}

		if ${This.StopMovement}
		{
			return MOVEMENT_STOP
		}

		if ${This.Arrived}
		{
			return MOVEMENT_ARRIVED
		}

		if ${Math.Calc[${LavishScript.RunningTime} - ${This.LastObstacleCheck}]} > 200 && ${This.GotObstacle} && !${This.StopObstacle}
		{
			return MOVEMENT_OBSTACLE
		}
	
		if ${This.Stuck}
		{
			return MOVEMENT_STUCK
		}
	
		if ${This.NeedMovement}
		{
			return MOVEMENT_MOVE
		}
	
		return MOVEMENT_NONE
	}

	method Pulse(int Priority)
	{
		This.CurrentState:Set[${This.State}]
		;This:EA[${This.State}]
		switch ${This.CurrentState}
		{
			case MOVEMENT_WAIT
				break
			case MOVEMENT_OBSTACLE
				This:AvoidObstacle
				break
			case MOVEMENT_MOVE
				This:MovePulse
				break
			case MOVEMENT_ARRIVED
				This:ResetDestination
				Event[MOVEMENT_ARRIVED]:Execute[${Me.X},${Me.Y}]
				This:Stop
				break
			case MOVEMENT_STOP
				This:ResetDestination
				This.Moving:Set[FALSE]
				This:Stop
				This.StopMovement:Set[FALSE]
				break
			case MOVEMENT_STUCK
				This:Stuck
				break
			case MOVEMENT_NONE
				break
		}
	}
	
	method Stuck()
	{
		if ${Math.Calc[${LavishScript.RunningTime} - ${This.LastStuckTime}]} > 5000
		{
			This:EA["Stuck omg"]
			This.LastStuckTime:Set[${LavishScript.RunningTime}]
			This.stuck:Inc
			This:Jump
		}
	}

	method ResetDestination()
	{
		This.StopObstacle:Set[FALSE]
		This.Destination.X:Set[0]
		This.Destination.Y:Set[0]
	}

	member DestinationDistance()
	{
		return ${Math.Distance[${Me.X},${Me.Y},${This.Destination.X},${This.Destination.Y}]}
	}

	method ToPoint(float X, float Y, float Distance, bool Idle = FALSE)
	{
		This:ResetDestination

		This.Moving:Set[TRUE]
		This.Mode:Set["point"]
		This.From:Set[FALSE]
		This.Precision:Set[${Distance}]

		This.Start.X:Set[${Me.X}]
		This.Start.Y:Set[${Me.Y}]
		
		This.Destination.X:Set[${X}]
		This.Destination.Y:Set[${Y}]

		if ${Idle}
		{
			This.MovingIdle:Set[TRUE]
			This:EA["To Point | Idle | ${Distance} @ ${X} ${Y}"]
		}
		else
		{
			This.MovingIdle:Set[FALSE]
			This:EA["To Point | ${Distance} @ ${X} ${Y}"]
		}
	}

	method FromPoint(float X, float Y, float Distance)
	{
		This:ResetDestination
		
		This.Moving:Set[TRUE]
		This.MovingIdle:Set[FALSE]
		This.Mode:Set["point"]
		This.From:Set[TRUE]
		This.Precision:Set[${Distance}]

		This.Start.X:Set[${Me.X}]
		This.Start.Y:Set[${Me.Y}]
		
		This.Destination.X:Set[${X}]
		This.Destination.Y:Set[${Y}]

		This:EA["From Point | ${Distance} @ ${X} ${Y}"]
	}

	method ToTarget(float Distance, bool Combat)
	{
		This:ResetDestination

		if !${Me.Target(exists)}
		{
			return
		}
		
		This.Moving:Set[TRUE]
		This.MovingIdle:Set[FALSE]
		This.Mode:Set["target"]
		This.From:Set[FALSE]
		This.Precision:Set[${Distance}]

		This.Start.X:Set[${Me.X}]
		This.Start.Y:Set[${Me.Y}]
		
		This.Destination.X:Set[${Me.Target.X}]
		This.Destination.Y:Set[${Me.Target.Y}]

		if ${Combat}
		{
			This.StopObstacle:Set[TRUE]
			;This:EA["To Target | Combat : ${Target.Name} | ${Distance} @ ${Me.Target.X} ${Me.Target.Y}"]
		}
		else
		{
			This:EA["To Target: ${Me.Target.Name} | ${Distance} @ ${Me.Target.X} ${Me.Target.Y}"]
		}
	}

	method FromTarget(float Distance)
	{
		This:ResetDestination

		if !${Me.Target(exists)}
		{
			return
		}
		
		This.Moving:Set[TRUE]
		This.MovingIdle:Set[FALSE]
		This.Mode:Set["target"]
		This.From:Set[TRUE]
		This.Precision:Set[${Distance}]

		This.Start.X:Set[${Me.X}]
		This.Start.Y:Set[${Me.Y}]
		
		This.Destination.X:Set[${Me.Target.X}]
		This.Destination.Y:Set[${Me.Target.Y}]

		This:EA["From Target | ${Me.Target.Name} | ${Distance} @ ${Me.Target.X} ${Me.Target.Y}"]
	}

	method ToPointIdle(float X, float Y)
	{
		This:ToPoint[${X},${Y},100.000000,TRUE]
	}

	method ToPointMelee(float X, float Y)
	{
		This:ToPoint[${X},${Y},100.000000]
	}

	method ToPointMidRanged(float X, float Y)
	{
		This:ToPoint[${X},${Y},700.000000]
	}

	method ToPointRanged(float X, float Y)
	{
		This:ToPoint[${X},${Y},950.000000]
	}

	method FromPointMelee(float X, float Y)
	{
		This:FromPoint[${X},${Y},100.000000]
	}

	method FromPointMidRanged(float X, float Y)
	{
		This:FromPoint[${X},${Y},700.000000]
	}

	method FromPointRanged(float X, float Y)
	{
		This:FromPoint[${X},${Y},950.000000]
	}

	method ToTargetCombat()
	{
		This:ToTarget[100.000000,TRUE]
	}

	method ToTargetMelee()
	{
		This:ToTarget[100.000000]
	}

	method ToTargetMidRanged()
	{
		This:ToTarget[700.000000]
	}

	method ToTargetRanged()
	{
		This:ToTarget[950.000000]
	}

	method FromTargetMelee()
	{
		This:FromTarget[100.000000]
	}

	method FromTargetMidRanged()
	{
		This:FromTarget[700.000000]
	}

	method FromTargetRanged()
	{
		This:FromTarget[950.000000]
	}
	
	method Inject()
	{
		This:ResetDestination
		
		LavishScript:RegisterEvent[MOVEMENT_ARRIVED]
		Event[S_CORE_EVENT_PULSE]:AttachAtom[${This.ObjectName}:Pulse]
	}

	method Shutdown()
	{
		Event[S_CORE_EVENT_PULSE]:DetachAtom[${This.ObjectName}:Pulse]
		Event[MOVEMENT_ARRIVED]:Unregister
	}
}